__init__.py 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303
  1. import dataclasses
  2. import decimal
  3. import json as _json
  4. import typing as t
  5. import uuid
  6. from datetime import date
  7. from jinja2.utils import htmlsafe_json_dumps as _jinja_htmlsafe_dumps
  8. from werkzeug.http import http_date
  9. from ..globals import current_app
  10. from ..globals import request
  11. if t.TYPE_CHECKING:
  12. from ..app import Flask
  13. from ..wrappers import Response
  14. class JSONEncoder(_json.JSONEncoder):
  15. """The default JSON encoder. Handles extra types compared to the
  16. built-in :class:`json.JSONEncoder`.
  17. - :class:`datetime.datetime` and :class:`datetime.date` are
  18. serialized to :rfc:`822` strings. This is the same as the HTTP
  19. date format.
  20. - :class:`uuid.UUID` is serialized to a string.
  21. - :class:`dataclasses.dataclass` is passed to
  22. :func:`dataclasses.asdict`.
  23. - :class:`~markupsafe.Markup` (or any object with a ``__html__``
  24. method) will call the ``__html__`` method to get a string.
  25. Assign a subclass of this to :attr:`flask.Flask.json_encoder` or
  26. :attr:`flask.Blueprint.json_encoder` to override the default.
  27. """
  28. def default(self, o: t.Any) -> t.Any:
  29. """Convert ``o`` to a JSON serializable type. See
  30. :meth:`json.JSONEncoder.default`. Python does not support
  31. overriding how basic types like ``str`` or ``list`` are
  32. serialized, they are handled before this method.
  33. """
  34. if isinstance(o, date):
  35. return http_date(o)
  36. if isinstance(o, (decimal.Decimal, uuid.UUID)):
  37. return str(o)
  38. if dataclasses and dataclasses.is_dataclass(o):
  39. return dataclasses.asdict(o)
  40. if hasattr(o, "__html__"):
  41. return str(o.__html__())
  42. return super().default(o)
  43. class JSONDecoder(_json.JSONDecoder):
  44. """The default JSON decoder.
  45. This does not change any behavior from the built-in
  46. :class:`json.JSONDecoder`.
  47. Assign a subclass of this to :attr:`flask.Flask.json_decoder` or
  48. :attr:`flask.Blueprint.json_decoder` to override the default.
  49. """
  50. def _dump_arg_defaults(
  51. kwargs: t.Dict[str, t.Any], app: t.Optional["Flask"] = None
  52. ) -> None:
  53. """Inject default arguments for dump functions."""
  54. if app is None:
  55. app = current_app
  56. if app:
  57. cls = app.json_encoder
  58. bp = app.blueprints.get(request.blueprint) if request else None # type: ignore
  59. if bp is not None and bp.json_encoder is not None:
  60. cls = bp.json_encoder
  61. # Only set a custom encoder if it has custom behavior. This is
  62. # faster on PyPy.
  63. if cls is not _json.JSONEncoder:
  64. kwargs.setdefault("cls", cls)
  65. kwargs.setdefault("cls", cls)
  66. kwargs.setdefault("ensure_ascii", app.config["JSON_AS_ASCII"])
  67. kwargs.setdefault("sort_keys", app.config["JSON_SORT_KEYS"])
  68. else:
  69. kwargs.setdefault("sort_keys", True)
  70. kwargs.setdefault("cls", JSONEncoder)
  71. def _load_arg_defaults(
  72. kwargs: t.Dict[str, t.Any], app: t.Optional["Flask"] = None
  73. ) -> None:
  74. """Inject default arguments for load functions."""
  75. if app is None:
  76. app = current_app
  77. if app:
  78. cls = app.json_decoder
  79. bp = app.blueprints.get(request.blueprint) if request else None # type: ignore
  80. if bp is not None and bp.json_decoder is not None:
  81. cls = bp.json_decoder
  82. # Only set a custom decoder if it has custom behavior. This is
  83. # faster on PyPy.
  84. if cls not in {JSONDecoder, _json.JSONDecoder}:
  85. kwargs.setdefault("cls", cls)
  86. def dumps(obj: t.Any, app: t.Optional["Flask"] = None, **kwargs: t.Any) -> str:
  87. """Serialize an object to a string of JSON.
  88. Takes the same arguments as the built-in :func:`json.dumps`, with
  89. some defaults from application configuration.
  90. :param obj: Object to serialize to JSON.
  91. :param app: Use this app's config instead of the active app context
  92. or defaults.
  93. :param kwargs: Extra arguments passed to :func:`json.dumps`.
  94. .. versionchanged:: 2.0.2
  95. :class:`decimal.Decimal` is supported by converting to a string.
  96. .. versionchanged:: 2.0
  97. ``encoding`` is deprecated and will be removed in Flask 2.1.
  98. .. versionchanged:: 1.0.3
  99. ``app`` can be passed directly, rather than requiring an app
  100. context for configuration.
  101. """
  102. _dump_arg_defaults(kwargs, app=app)
  103. return _json.dumps(obj, **kwargs)
  104. def dump(
  105. obj: t.Any, fp: t.IO[str], app: t.Optional["Flask"] = None, **kwargs: t.Any
  106. ) -> None:
  107. """Serialize an object to JSON written to a file object.
  108. Takes the same arguments as the built-in :func:`json.dump`, with
  109. some defaults from application configuration.
  110. :param obj: Object to serialize to JSON.
  111. :param fp: File object to write JSON to.
  112. :param app: Use this app's config instead of the active app context
  113. or defaults.
  114. :param kwargs: Extra arguments passed to :func:`json.dump`.
  115. .. versionchanged:: 2.0
  116. Writing to a binary file, and the ``encoding`` argument, is
  117. deprecated and will be removed in Flask 2.1.
  118. """
  119. _dump_arg_defaults(kwargs, app=app)
  120. _json.dump(obj, fp, **kwargs)
  121. def loads(
  122. s: t.Union[str, bytes],
  123. app: t.Optional["Flask"] = None,
  124. **kwargs: t.Any,
  125. ) -> t.Any:
  126. """Deserialize an object from a string of JSON.
  127. Takes the same arguments as the built-in :func:`json.loads`, with
  128. some defaults from application configuration.
  129. :param s: JSON string to deserialize.
  130. :param app: Use this app's config instead of the active app context
  131. or defaults.
  132. :param kwargs: Extra arguments passed to :func:`json.loads`.
  133. .. versionchanged:: 2.0
  134. ``encoding`` is deprecated and will be removed in Flask 2.1. The
  135. data must be a string or UTF-8 bytes.
  136. .. versionchanged:: 1.0.3
  137. ``app`` can be passed directly, rather than requiring an app
  138. context for configuration.
  139. """
  140. _load_arg_defaults(kwargs, app=app)
  141. return _json.loads(s, **kwargs)
  142. def load(fp: t.IO[str], app: t.Optional["Flask"] = None, **kwargs: t.Any) -> t.Any:
  143. """Deserialize an object from JSON read from a file object.
  144. Takes the same arguments as the built-in :func:`json.load`, with
  145. some defaults from application configuration.
  146. :param fp: File object to read JSON from.
  147. :param app: Use this app's config instead of the active app context
  148. or defaults.
  149. :param kwargs: Extra arguments passed to :func:`json.load`.
  150. .. versionchanged:: 2.0
  151. ``encoding`` is deprecated and will be removed in Flask 2.1. The
  152. file must be text mode, or binary mode with UTF-8 bytes.
  153. """
  154. _load_arg_defaults(kwargs, app=app)
  155. return _json.load(fp, **kwargs)
  156. def htmlsafe_dumps(obj: t.Any, **kwargs: t.Any) -> str:
  157. """Serialize an object to a string of JSON with :func:`dumps`, then
  158. replace HTML-unsafe characters with Unicode escapes and mark the
  159. result safe with :class:`~markupsafe.Markup`.
  160. This is available in templates as the ``|tojson`` filter.
  161. The returned string is safe to render in HTML documents and
  162. ``<script>`` tags. The exception is in HTML attributes that are
  163. double quoted; either use single quotes or the ``|forceescape``
  164. filter.
  165. .. versionchanged:: 2.0
  166. Uses :func:`jinja2.utils.htmlsafe_json_dumps`. The returned
  167. value is marked safe by wrapping in :class:`~markupsafe.Markup`.
  168. .. versionchanged:: 0.10
  169. Single quotes are escaped, making this safe to use in HTML,
  170. ``<script>`` tags, and single-quoted attributes without further
  171. escaping.
  172. """
  173. return _jinja_htmlsafe_dumps(obj, dumps=dumps, **kwargs)
  174. def htmlsafe_dump(obj: t.Any, fp: t.IO[str], **kwargs: t.Any) -> None:
  175. """Serialize an object to JSON written to a file object, replacing
  176. HTML-unsafe characters with Unicode escapes. See
  177. :func:`htmlsafe_dumps` and :func:`dumps`.
  178. """
  179. fp.write(htmlsafe_dumps(obj, **kwargs))
  180. def jsonify(*args: t.Any, **kwargs: t.Any) -> "Response":
  181. """Serialize data to JSON and wrap it in a :class:`~flask.Response`
  182. with the :mimetype:`application/json` mimetype.
  183. Uses :func:`dumps` to serialize the data, but ``args`` and
  184. ``kwargs`` are treated as data rather than arguments to
  185. :func:`json.dumps`.
  186. 1. Single argument: Treated as a single value.
  187. 2. Multiple arguments: Treated as a list of values.
  188. ``jsonify(1, 2, 3)`` is the same as ``jsonify([1, 2, 3])``.
  189. 3. Keyword arguments: Treated as a dict of values.
  190. ``jsonify(data=data, errors=errors)`` is the same as
  191. ``jsonify({"data": data, "errors": errors})``.
  192. 4. Passing both arguments and keyword arguments is not allowed as
  193. it's not clear what should happen.
  194. .. code-block:: python
  195. from flask import jsonify
  196. @app.route("/users/me")
  197. def get_current_user():
  198. return jsonify(
  199. username=g.user.username,
  200. email=g.user.email,
  201. id=g.user.id,
  202. )
  203. Will return a JSON response like this:
  204. .. code-block:: javascript
  205. {
  206. "username": "admin",
  207. "email": "admin@localhost",
  208. "id": 42
  209. }
  210. The default output omits indents and spaces after separators. In
  211. debug mode or if :data:`JSONIFY_PRETTYPRINT_REGULAR` is ``True``,
  212. the output will be formatted to be easier to read.
  213. .. versionchanged:: 2.0.2
  214. :class:`decimal.Decimal` is supported by converting to a string.
  215. .. versionchanged:: 0.11
  216. Added support for serializing top-level arrays. This introduces
  217. a security risk in ancient browsers. See :ref:`security-json`.
  218. .. versionadded:: 0.2
  219. """
  220. indent = None
  221. separators = (",", ":")
  222. if current_app.config["JSONIFY_PRETTYPRINT_REGULAR"] or current_app.debug:
  223. indent = 2
  224. separators = (", ", ": ")
  225. if args and kwargs:
  226. raise TypeError("jsonify() behavior undefined when passed both args and kwargs")
  227. elif len(args) == 1: # single args are passed directly to dumps()
  228. data = args[0]
  229. else:
  230. data = args or kwargs
  231. return current_app.response_class(
  232. f"{dumps(data, indent=indent, separators=separators)}\n",
  233. mimetype=current_app.config["JSONIFY_MIMETYPE"],
  234. )